using System;
using System.Collections.Generic;
using System.Text;
using Barrage.Global;
using SlimDX.Direct3D9;
using SlimDX;

namespace Barrage.Render
{
    /// <summary>
    /// RenderProcess is the main class for performing a render chain.
    /// It holds a list of render passes, consisting of a scene and a render target.
    /// When Render is called, RenderProcess performs the Render of each pass in order, usually using the render target of each pass as a source for the next (as a texture in some item of the scene)
    /// </summary>
    public class RenderProcess
    {
        /// <summary>
        /// List of render passes of the render process
        /// </summary>
        protected List<RenderPass> renderPasses = new List<RenderPass>();

        Viewport viewport;
        /// <summary>
        /// Viewport in which the renderprocess is done
        /// </summary>

        public Viewport Viewport { get { return viewport; } set { viewport = value; } }
        /// <summary>
        /// The last render pass of the process
        /// </summary>
        protected RenderPass finalRenderPass;

        /// <summary>
        /// Returns the render target of the last render pass
        /// </summary>
        public Texture RenderTarget { get { return finalRenderPass.RenderTarget; } }

        /// <summary>
        /// The last render pass of the process
        /// </summary>
        public RenderPass FinalRenderPass { get { return finalRenderPass; } }

        /// <summary>
        /// Inits a render process with an empty render pass.
        /// The render pass' target will be the default backbuffer
        /// </summary>
        public RenderProcess()
        {
            initViewport();
            finalRenderPass = new RenderPass(false);
            finalRenderPass.DepthBuffer = null;
            finalRenderPass.MustClear = true;
        }

        /// <summary>
        /// Inits a render process with an empty render pass.
        /// Also adds the scene as source for the created render pass
        /// </summary>
        /// <param name="scene">A scene to be rendered on the process</param>
        public RenderProcess(Scene scene)
            : this()
        {
            addSource(scene);
        }

        /// <summary>
        /// Inits a render process with an empty render pass.
        /// The render pass' target will be the texture passed
        /// </summary>
        /// <param name="renderTarget">The texture to be used as render target</param>
        public RenderProcess(Texture renderTarget)
            : this()
        {
            finalRenderPass.RenderTarget = renderTarget;
        }

        /// <summary>
        /// Inits a render process with a render pass.
        /// The render pass' target will be the texture passed
        /// Also adds the scene as source for the created render pass
        /// </summary>
        /// <param name="renderTarget">The texture to be used as render target</param>
        /// <param name="scene">A scene to be rendered on the process</param>
        public RenderProcess(Texture renderTarget, Scene scene)
            : this(renderTarget)
        {
            addSource(scene);
        }

        /// <summary>
        /// Inits the render process with the passed render pass
        /// </summary>
        /// <param name="pass">The final render pass of the process</param>
        public RenderProcess(RenderPass pass)
        {
            initViewport();
            finalRenderPass = pass;
        }

        /// <summary>
        /// Inits the render process with the passed render pass and a source scene
        /// </summary>
        /// <param name="pass">The final render pass of the process</param>
        /// <param name="source">A scene to be rendered on the process</param>
        public RenderProcess(RenderPass pass,Scene source):this(pass)
        {
            addSource(source);
        }

        /// <summary>
        /// Adds a render pass to the render passes list
        /// </summary>
        /// <param name="pass">Render pass to be added</param>
        public void addIntermediatePass(RenderPass pass)
        {
            renderPasses.Add(pass);
        }
        
        /// <summary>
        /// Inits a viewport with the default values (render to the whole screen)
        /// </summary>
        protected void initViewport()
        {
            viewport = new Viewport();
            viewport.Height = Engine.DefaultRenderTargetDescription.Height;
            viewport.Width = Engine.DefaultRenderTargetDescription.Width;
            viewport.X = 0;
            viewport.Y = 0;
            viewport.MinZ = 0;
            viewport.MaxZ = 1;
        }

        /// <summary>
        /// Renders an unique pass
        /// </summary>
        /// <param name="p">Pass to be rendered</param>
        protected void RenderPass(RenderPass p)
        {
            Engine.Device.DepthStencilSurface = p.DepthBuffer;
            p.Begin();
            if (p.MustClear)
                Engine.Device.Clear(ClearFlags.Target | ClearFlags.ZBuffer, p.BackgroundColor, 1, 0);
            Engine.Device.SetRenderState(RenderState.ZEnable, p.ZCull);
            p.Render();
            p.End();

        }

        /// <summary>
        /// Performs the render chain
        /// </summary>
        public virtual void Render()
        {
            foreach(RenderPass p in renderPasses)
            {
                Engine.Device.SetRenderTarget(0, p.RenderTargetSurface);
                RenderPass(p);
            }
            if (finalRenderPass.RenderTarget!=null)
                Engine.Device.SetRenderTarget(0, finalRenderPass.RenderTargetSurface);
            else
                Engine.Device.SetRenderTarget(0, Engine.BackBuffer);
            Engine.Device.Viewport = this.viewport;
            RenderPass(finalRenderPass);
        }

        /// <summary>
        /// Adds a new source scene to the final render pass
        /// </summary>
        /// <param name="scene">scene to be added</param>
        public virtual void addSource(Scene scene)
        {
            finalRenderPass.addSource(scene);
        }
    }
}
